import React, { useState, useMemo, useCallback, useEffect } from 'react';
import { intersectionBy, xorBy, unionBy, isEqual } from 'lodash';
import {Checkbox, Col, Row} from 'antd';
import { CheckboxGroupProps, CheckboxProps } from 'antd/lib/checkbox';
import { RowProps } from 'antd/lib/row';

export enum SELECT_STATUS_ENUM {
  All, Some, None
}

export interface ItemType {
  id: string | number;
  name: string;
}

type Toggle = () => void;
type Append = (v: ItemType) => void;
type Remove = (v: ItemType) => void;

export function useSelectedStatus(data: ItemType[], value: ItemType[], setValue: (value: ItemType[]) => void):
[SELECT_STATUS_ENUM, Partial<CheckboxProps>, Toggle, Append, Remove] {
  const intersectionValues = useMemo(
    () => intersectionBy(value, data, 'id'), [
    value, data
  ]);
  const selectedStatus = intersectionValues.length === 0 ? SELECT_STATUS_ENUM.None : intersectionValues.length === data.length ? SELECT_STATUS_ENUM.All : SELECT_STATUS_ENUM.Some;;
  const clearCurrentAll = useCallback(() => {
    setValue(xorBy(value, intersectionValues, 'id'));
  }, [intersectionValues, setValue, value]);
  const selectAll = useCallback(() => {
    setValue(unionBy(value, data, 'id'));
  }, [data, setValue, value]);
  const toggle = useCallback(() => {
    switch (selectedStatus) {
      case SELECT_STATUS_ENUM.All:
        clearCurrentAll();
        break;
      case SELECT_STATUS_ENUM.Some:
      case SELECT_STATUS_ENUM.None:
        selectAll();
        break;
    }
  }, [clearCurrentAll, selectAll, selectedStatus])
  const append = useCallback((v: ItemType) => {
    setValue([...value, v])
  }, [setValue, value]);
  const remove = useCallback((v: ItemType) => {
    setValue(xorBy(value, [v], 'id'));
  }, [setValue, value]);
  const checkboxProps = useMemo<Partial<CheckboxProps>>(() => ({
    indeterminate: selectedStatus===SELECT_STATUS_ENUM.Some,
    checked: selectedStatus === SELECT_STATUS_ENUM.All ? true : false,
      onChange: toggle
  }), [selectedStatus, toggle])
  return [selectedStatus, checkboxProps, toggle, append, remove];
}

export function useRenderOptions(data: ItemType[], value: ItemType[], append: Append, remove: Remove): [
  CheckboxGroupProps['options'], ItemType['id'][]
] {
  const options = useMemo<CheckboxGroupProps['options']>(() => {
    return data.map(item => ({
      label: item.name,
      value: item.id,
      onChange(e) {
        if (e.target.checked) {
          append(item);
        } else {
          remove(item);
        }
      }
    }))
  }, [append, data, remove])
  const checkedIds = useMemo<ItemType['id'][]>(() => value.map(x => x.id), [value])
  return [options, checkedIds];
}

export interface PickerItemType extends Omit<RowProps, 'onChange'> {
  value?: ItemType[],
  onChange?: (value: ItemType[]) => void;
  data: ItemType[];
  label: string;
}

Picker.defaultProps = {
  gutter: [4, 4],
  style: {
    textAlign: 'left'
  }
}

Picker.displayName = 'ControllableCheckboxGroup';

function Picker({label, data, value, onChange, ...rest}: PickerItemType) {
  const [selectedValues, _setSelectedValues] = useState<ItemType[]>(value || []);

  // 处理选中值的变化
  const setSelectedValues = useCallback((v: ItemType[]) => {
    if (isEqual(selectedValues, v)) {
      return;
    }
    if (value == null) {
      // 非受控组件
      _setSelectedValues(v);
    } else if (!isEqual(value, v)) {
      // 受控组件
      onChange && onChange(v);
    }
    
  }, [onChange, selectedValues, value]);

  // 受控组件同步 value -> selectedValues
  useEffect(() => {
    if (value == null || isEqual(selectedValues, value)) {
      return;
    }
    _setSelectedValues(value);
  }, [selectedValues, value]);

  // 当前选中的状态
  const [,checkboxProps,, append, remove] = useSelectedStatus(data, selectedValues, setSelectedValues);

  // picker 可选择内容选项
  const [options, checkedIds] = useRenderOptions(data, selectedValues, append, remove);
  
  return <Row {...rest}>
    <Col span={24}>
      <Checkbox {...checkboxProps}>
        {label}
      </Checkbox>
    </Col>
    <Col span={24}>
      <Checkbox.Group options={options} value={checkedIds} />
    </Col>
  </Row>
}

export default React.memo(Picker);
